#pragma once
#include <string>
#include <stdexcept>
#include <cstring>
#include <opencv2/opencv.hpp>

#include <gst/gst.h>
#include <gst/app/gstappsrc.h>

namespace rtsp {

// GStreamer + mpph264enc 的简易 RTSP 推流器（单头文件）
class Pusher {
public:
    // url 例：rtsp://127.0.0.1:8554/mystream
    // W/H: 期望的编码输出分辨率；FPS: 帧率；bps: 码率；gop: 关键帧间隔
    Pusher(const std::string& url, int W, int H, int FPS = 30, int bps = 4'000'000, int gop = 60,
           const std::string& encoder = "mpph264enc", const std::string& proto = "tcp")
        : url_(url), W_(W), H_(H), FPS_(FPS)
    {
        // 多次调用也安全；若你项目里已初始化，可删掉这行
        gst_init(nullptr, nullptr);

        // 允许 x264 备选：如果没有 mpp，可把 encoder 传入 "x264enc tune=zerolatency speed-preset=veryfast"
        std::string pipe =
            "appsrc name=mysrc is-live=true format=time do-timestamp=true "
            "caps=video/x-raw,format=BGR,width=" + std::to_string(W_) +
            ",height=" + std::to_string(H_) + ",framerate=" + std::to_string(FPS_) + "/1 ! "
            "videoconvert ! video/x-raw,format=NV12 ! " +
            encoder + " bps=" + std::to_string(bps) + " gop=" + std::to_string(gop) + " ! "
            "h264parse config-interval=1 ! "
            "rtspclientsink location=" + url_ + " protocols=" + proto;

        GError* err = nullptr;
        pipeline_ = gst_parse_launch(pipe.c_str(), &err);
        if (err) {
            std::string msg = "gst_parse_launch: ";
            msg += err->message ? err->message : "unknown error";
            g_error_free(err);
            throw std::runtime_error(msg);
        }

        appsrc_ = gst_bin_get_by_name(GST_BIN(pipeline_), "mysrc");
        if (!appsrc_) throw std::runtime_error("cannot get appsrc");

        // 降低延迟的小参数（可选）
        g_object_set(G_OBJECT(appsrc_), "format", GST_FORMAT_TIME, NULL);

        if (gst_element_set_state(pipeline_, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
            throw std::runtime_error("pipeline set PLAYING failed");
        }

        dur_ = gst_util_uint64_scale_int(1, GST_SECOND, FPS_);
        ts_ = 0;
    }

    // 推一帧 BGR 图像（任意大小，内部会 resize 到构造时的 W×H）
    void push(const cv::Mat& bgr)
    {
        if (bgr.empty()) return;

        // 保证尺寸与 caps 一致（caps 里锁定为 W_×H_）
        cv::Mat sized;
        if (bgr.cols != W_ || bgr.rows != H_) {
            cv::resize(bgr, sized, cv::Size(W_, H_), 0, 0, cv::INTER_AREA);
        } else {
            sized = bgr;
        }
        if (sized.type() != CV_8UC3) {
            cv::Mat tmp; sized.convertTo(tmp, CV_8UC3); sized = tmp;
        }
        cv::Mat contiguous = sized.isContinuous() ? sized : sized.clone();

        const size_t bytes = contiguous.total() * contiguous.elemSize();

        GstBuffer* buf = gst_buffer_new_allocate(nullptr, bytes, nullptr);
        if (!buf) throw std::runtime_error("gst_buffer_new_allocate failed");

        GstMapInfo map;
        if (!gst_buffer_map(buf, &map, GST_MAP_WRITE)) {
            gst_buffer_unref(buf);
            throw std::runtime_error("gst_buffer_map failed");
        }
        std::memcpy(map.data, contiguous.data, bytes);
        gst_buffer_unmap(buf, &map);

        GST_BUFFER_PTS(buf)      = ts_;
        GST_BUFFER_DTS(buf)      = ts_;
        GST_BUFFER_DURATION(buf) = dur_;
        ts_ += dur_;

        GstFlowReturn ret;
        g_signal_emit_by_name(appsrc_, "push-buffer", buf, &ret);
        gst_buffer_unref(buf);

        if (ret != GST_FLOW_OK) {
            throw std::runtime_error("push-buffer flow != OK");
        }
    }

    ~Pusher() {
        if (appsrc_)  { g_signal_emit_by_name(appsrc_, "end-of-stream", nullptr); }
        if (pipeline_){ gst_element_set_state(pipeline_, GST_STATE_NULL); }
        if (appsrc_)  { gst_object_unref(appsrc_);  appsrc_ = nullptr; }
        if (pipeline_){ gst_object_unref(pipeline_); pipeline_ = nullptr; }
    }

    // 工具：返回当前 caps 设定的输出尺寸（便于你在外部做预览/布局）
    int width()  const { return W_; }
    int height() const { return H_; }
    int fps()    const { return FPS_; }

private:
    std::string url_;
    int W_, H_, FPS_;
    guint64 ts_ = 0, dur_ = 0;

    GstElement* pipeline_ = nullptr;
    GstElement* appsrc_   = nullptr;
};

} // namespace rtsp
